/**
 * \file PluginManager.h
 * \brief PluginManager declaration class
 * \author Edouard ROGE
 * \date 16 Mars 2013
 */
#ifndef _PLUGINMANAGER_
#define _PLUGINMANAGER_

#include <vector>
#include "Plugin.h"
#include "PluginObject.h"
#include "Core/Pattern/Singleton.h"
#include "Core/Error/Error.h"
#include "Core/Log/LogManager.h"

namespace Core
{
	namespace Plugin
	{
		/**
		 * \class PluginManager
		 * \brief Load / unload plug-ins
		 */
		class PluginManager : public Singleton<PluginManager>
		{
		public:
			/**
			 * \fn PluginManager();
			 * \brief Default constructor
			 */
			PluginManager();

			/**
			 * \fn virtual ~PluginManager();
			 * \brief Destructor
			 */
			virtual ~PluginManager();

			/**
			* \fn template <class T> T * RegisterPlugin(Plugin::Type _ePluginType, const std::string & _strPluginName);
			* \param _ePluginType Plug-in type
			* \param _strPluginName Path of the plug-in to load
			* \return Loaded plugin
			* \brief Register and load a plug-in to the plug-in manager
			*/
			template <class T> T * RegisterPlugin(Type _ePluginType, const std::string & _strPluginName);

			/**
			 * \fn error UnregisterPlugin(Plugin::Type _ePluginType);
			 * \param _ePluginType Type of the plug-in to unload
			 * \return Error if occurs
			 * \brief Unregister and unload a plug-in from the plug-in manager
			 */
			error UnregisterPlugin(Type _ePluginType);

			/**
			 * \fn Plugin * GetPlugin(Plugin::Type _ePluginType) const;
			 * \param _ePluginType type of plugin to retrieve from
			 * \return Plugin corresponding to the type
			 * \biref Get the plugin according to its type
			 */
			Plugin * GetPlugin(Type _ePluginType) const;

			/**
			 * \fn PluginInterface * GetPluginInterface(Plugin::Type _ePluginType) const;
			 * \param _ePluginType type of plugin to retrieve from
			 * \return PluginInterface corresponding to the type
			 * \biref Get the plugin interface according to its type
			 */
			IPlugin * GetPluginInterface(Type _ePluginType) const;

		private:

			/** List of available plug-ins */
			std::vector<Plugin*> m_Plugins;
		};


		template <class T>
		T * PluginManager::RegisterPlugin(Type _ePluginType, const std::string & _strPluginName)
		{
			if (m_Plugins[_ePluginType] != NULL)
			{
				LOG_ERROR("Cannot load %s, a plug-in of the same type is already loaded", _strPluginName.c_str());
				return NULL;
			}

#ifdef _WIN32
			std::string strFullPathPlugin = _strPluginName + ".dll";
#else
			std::string strFullPathPlugin = "./" + _strPluginName + ".so";
#endif

			m_Plugins[_ePluginType] = new PluginObject<T>();

			error e = m_Plugins[_ePluginType]->Load(strFullPathPlugin);

			if (e != errOK)
			{
				delete m_Plugins[_ePluginType];
				m_Plugins[_ePluginType] = NULL;
				return NULL;
			}

			return (T*)m_Plugins[_ePluginType]->GetInterface();
		}
	}
}

#endif // _PLUGINMANAGER_
